//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

#include <util.h>
#include <udelay.h>
#include <gpio.h>
#include <bulverde.h>
#include "testaudio.h"

// AC97 control registers
#define POCR        __REG( 0x40500000 )          //     PCM Out Control register
#define PCMICR      __REG( 0x40500004 )          //     PCM In Control register
#define MCCR        __REG( 0x40500008 )          //     Mic In Control register
#define GCR         __REG( 0x4050000C )          //     Global Control register
#define POSR        __REG( 0x40500010 )          //     PCM Out Status register
#define PCMISR      __REG( 0x40500014 )          //     PCM In Status register
#define MCSR        __REG( 0x40500018 )          //     Mic In Status register
#define GSR         __REG( 0x4050001C )          //     Global Status register
#define CAR         __REG( 0x40500020 )          //     CODEC Access register
#define PCDR        __REG( 0x40500040 )          //     PCM FIFO Data register
#define MCDR        __REG( 0x40500060 )          //     Mic-in FIFO Data register

#define STATE_AUDIOOUT          0x00000001
#define STATE_AUDIOIN           0x00000002

// volume type
#define VOLUMETYPE_SPEAKER      0
#define VOLUMETYPE_HEADSET      1
#define VOLUMETYPE_MONO         2
#define VOLUMETYPE_MIC          3
#define VOLUMETYPE_DACGAIN      4
#define VOLUMETYPE_BTHEADSET    5

int Ac97_Reset();
int Ac97_Read(unsigned char Offset, unsigned short * Data);
int  Ac97_Write(unsigned char Offset, unsigned short  Data);

#if 0
void DumpAc97LinkRegs()
{
    printk("Dump AC97Link registers\n");

    printk("GCR        =%x\n", GCR);
    printk("GSR        =%x\n", GSR);
    printk("POCR       =%x\n", POCR);
    printk("POSR       =%x\n", POSR);
    printk("PCMICR     =%x\n", PCMICR);
    printk("PCMISR     =%x\n", PCMISR);
    printk("CAR        =%x\n", CAR);
}


void DumpAC97Regs(void)
{
    unsigned char i;
    unsigned int16 Data;

    printk("*********************************************\n");
    printk("DumpAC97Regs below:\n");

    for (i = 0; i < 0x7e; i += 2) {
        Ac97_Read(i, &Data);
        printk("reg[0x%x]=0x%04x  ", i, Data);
        if ((i % 8) == 0) printk("\n");
    }
    printk("\n*********************************************\n");
}

// Set codec register bits when the corresponding bits of val is set.
static void Codec_SetRegBits(int idx, unsigned short val)
{
    unsigned short value;

    Ac97_Read(idx, &value);
    value |= val;
    Ac97_Write(idx, value);
}
#endif

// Clear codec register bits when the corresponding bits of val is set.
static void Codec_ClearRegBits(int idx, unsigned short val)
{
    unsigned short value;

    Ac97_Read(idx, &value);
    value &= (~val);
    Ac97_Write(idx, value);
}

// Write codec register for special bits, using mask.
// reg[idx] = (reg[idx] & (~mask)) | val
static void Codec_WriteRegValue(int idx, unsigned short val, unsigned short mask)
{
    unsigned short value;

    Ac97_Read(idx, &value);
    value &= (~mask);
    value |= val;
    Ac97_Write(idx, value);
}


void Codec_SetGpios()
{
    xs_setgpio_dir(52, GPIO_PIN_OUT);
    GPSR1 |= 1<< 20;
    udelay(50000);

    //reset pin
    xs_setgpio_dir(113, GPIO_PIN_OUT);
    xs_setgpio_AFR(113, GPIO_AS_AF2);

    CKEN |= CKEN2_AC97;
    OSCC |= 1<<3;
    udelay(10000);

    // AC97_BITCLK
    xs_setgpio_dir(28, GPIO_PIN_IN);
    xs_setgpio_AFR(28, GPIO_AS_AF1);
//    GPCR0 &= 1 << 28;//bitclock low
//    GPSR0 &=0<<28;//bitclock low
    GPCR0 |=  1 << 28;//bitclock low

    // AC97_SYNC
    xs_setgpio_dir(31, GPIO_PIN_OUT);
    xs_setgpio_AFR(31, GPIO_AS_AF2);
    GPCR0 |= 1 << 31; // When RESETB applied, SYNC must be held low (WM9713)
    udelay(500);

    GPSR0 |= 1 << 28;//bitclock high

    // AC97_SDATAOUT
    xs_setgpio_dir(30, GPIO_PIN_OUT);
    xs_setgpio_AFR(30, GPIO_AS_AF2);

    // AC97_SDATAIN
    xs_setgpio_dir(29, GPIO_PIN_IN);
    xs_setgpio_AFR(29, GPIO_AS_AF1);

//    xs_setgpio_dir(113, GPIO_PIN_OUT);
//    xs_setgpio_AFR(113, GPIO_AS_AF2);

    xs_setgpio_dir(88, GPIO_PIN_OUT); //B_PA_SHUTDN
    GPCR2 |= 0x01000000; // Close it defaulty

    udelay(10);
}

int Codec_Init()
{
    Ac97_Write(0x26, 0L); // Power control
    udelay(10);

    // probing AC97 codec, AC97 2.0 says that bit 15 of register 0x00 (reset) should
    // be read zero.
    unsigned short codecID;
    Ac97_Read(0x00, &codecID);
    if (codecID & 0x8000) {
        printk("Error when probing AC97 codec.\n");
        return 1;
    }

//    unsigned short id1, id2;
//    Ac97_Read(0x7c, &id1);
//    Ac97_Read(0x7e, &id2);


#if 1
    // VREF should be always open for touch's normal operation
    Ac97_Write(0x26, 0x6700);
    Ac97_Write(0x24, 0x0000);
    Ac97_Write(0x3c, 0x1aff);
    Ac97_Write(0x3e, 0xffff);
#else
    Ac97_Write(0x26, 0x0000 );
    Ac97_Write(0x24, 0x0000 );
    Ac97_Write(0x3c, 0x0000);
    Ac97_Write(0x3e, 0x0000);
#endif

    //The following to set the 0x2a
    Ac97_Write(0x2a, 0x0411);

    /* p3 patch */
   Ac97_Write(0x16, 0x0603);
    /* bass control  */
    // Ac97_Write(0x08, 0x814b );
    /* 3D */

    Ac97_Write(0x20, 0x0f0f ); // Default close bass & treble
//    Ac97_Write(0x20, 0xa040 );
#ifdef MICBIAS_AUTOOPEN
    Ac97_Write(0x22, 0x0040 );
    Ac97_Write(0x26, 0x6300 ); // Open PR2 (input PGAs)
    Ac97_Write(0x3e, 0xbfff);
#endif
    /* ALC control */
    Ac97_Write(0x1c, 0x8f8f );
    Ac97_Write(0x62, 0x3eb5 );
#if 0
    //The following is added by Bill to set the 0x36
    Ac97_Write(0x36, 0xa503);
#endif

    // debug
    Ac97_Write(0x0c, 0xe606);

    return 0;
}

// Write the sample rate value to the codec
// Parameters:
//      nSampleRate: the sample rate
int Codec_SetSampleRate(unsigned int rate, unsigned int dir)
{
    switch(rate) {
        case 8000:
        case 11025:
        case 12000:
        case 16000:
        case 22050:
        case 24000:
        case 32000:
        case 44100:
        case 48000:
                break;
            default:
                // No implemented
                return 1;
    }
    Ac97_Write(dir == STATE_AUDIOOUT ? 0x2c : 0x32, rate);

    if(rate < 8000) {
        Ac97_Write(0x44, 0x0080);
    }
    else
    {
        // PLL_IN clock = 13Mhz, PLL_OUT clock = 98.304Mhz
        // N[3:0] = 7h, K[21:0] = 23f488h
        Ac97_Write(0x46,  0x7408 );
        Ac97_Write(0x46,  0x7418 );
        Ac97_Write(0x46,  0x7424 );
        Ac97_Write(0x46,  0x743f );
        Ac97_Write(0x46,  0x7443 );
        Ac97_Write(0x46,  0x7452 );
        Ac97_Write(0x46,  0x7460 );
        Ac97_Write(0x46,  0x7470 );
        Ac97_Write(0x44,  0x0000);
    }

    return 0;
}

void Codec_OpenAmplifier(int bOpen)
{
    if (bOpen) {
        GPSR2 |= 0x01000000; // Open
    }
    else {
        GPCR2 |= 0x01000000; // Close
    }
}

#define CODECVOLUME_LEVEL 16
// Used for speaker and mono
const unsigned short c_speakerVolumeTable[] = {
    //0,  1,  2,  3,  4,  5,  6,  7, 8, 9, 10, 11, 12, 13, 14, 15     input
     31, 18, 15, 12, 10,  8,  7,  6, 5, 4,  3,  3,  2,  2,  1,  0  // ouput
    };
// Used for headset
const unsigned short c_headsetVolumeTable[] = {
    //0,  1,  2,  3,  4,  5,  6,  7, 8, 9, 10, 11, 12, 13, 14, 15     input
     31, 20, 17, 14, 12, 10,  9,  8, 7, 6,  5,  4,  3,  2,  1,  0  // ouput
    };
#define GETCODECVOLUME_SPEAKER(volume) \
    (c_speakerVolumeTable[volume / CODECVOLUME_LEVEL])
#define GETCODECVOLUME_HEADSET(volume) \
    (c_headsetVolumeTable[volume / CODECVOLUME_LEVEL])
#define GETCODECVOLUME_MONO(volume) \
    (c_speakerVolumeTable[volume / CODECVOLUME_LEVEL])


int Codec_SetVolume(unsigned char left, unsigned char right, unsigned int volType)
{
    unsigned short volume = 0, RegLeft, RegRight;
    int ec = 0;

    // BUGBUG: The channels are upside down in hardware
    if (VOLUMETYPE_SPEAKER == volType) {
        // Speaker can only support 1 channel to output now.
        // Use the maximal volume of 2 channels to set!
        // So the speaker can talk normally
        // when any of the two channels are set rightly.
        if (left > right) right = left;
        else left = right;
    }
    else {
        unsigned char temp;
        temp = left;
        left = right;
        right = temp;
    }

    switch (volType) {
    case VOLUMETYPE_SPEAKER:
        Codec_OpenAmplifier(1);

        // Power Management: Open speaker mixer
        Codec_ClearRegBits(0x3c, 0x0002);
        // Power Management: Open SPKL and SPKR output PGA
        Codec_ClearRegBits(0x3e, 0x0180);
        // Open DAC to speaker mixer path
        Codec_ClearRegBits(0x0c, 0x4000);

        // INV2: SPKMIX
        Codec_WriteRegValue(0x1e, 0x0800, 0xfe00);
        //Ac97_Write(0x1e, 0x0800);
        // SPKL & SPKR PGA input select (SPKMIX & INV2)
        Codec_WriteRegValue(0x1c, 0x1c00, 0x3f00);
        // Codec_WriteRegValue(0x1c, 0x2400, 0x3f00);

//        RegLeft   = GETCODECVOLUME_SPEAKER(left);
//        RegRight  = GETCODECVOLUME_SPEAKER(right);

        RegLeft = left;
        RegRight = right;

        volume = RegRight | (RegLeft << 8);
        if( left == 0 ) volume |= 0x8000; //mute
        if( right == 0 ) volume |= 0x0080; //mute
        Ac97_Write(0x02, volume);
        break;

    case VOLUMETYPE_HEADSET:
        // Power Management: Open left and right headphone mixer
        Codec_ClearRegBits(0x3c, 0x000c);
        // Power Management: Open HPL and HPR output PGA
        Codec_ClearRegBits(0x3e, 0x0600);
        // Open DAC to headphone mixer path
        Codec_ClearRegBits(0x0c, 0x8000);
        // HPL & HPR PGA input select (HPMIXL & HPMIXR)
        Codec_WriteRegValue(0x1c, 0x00a0, 0x00f0);

        RegLeft   = GETCODECVOLUME_HEADSET(left);
        RegRight  = GETCODECVOLUME_HEADSET(right);
        volume = RegRight | (RegLeft << 8);
        if( left == 0 ) volume |= 0x8000; //mute
        if( right == 0 ) volume |= 0x0080; //mute
        Ac97_Write(0x04, volume);
        break;

    default:
        ec = 1;
        break;
    }

    return ec;
}

void Codec_OpenAc97DAC()
{
    // Power Management: Open PR6, PR5, PR4, PR3, PR2, PR1
    Codec_ClearRegBits(0x26, 0x7e00);
    // Power Management: Open VREF, PLL, DACL, DACR
    Codec_ClearRegBits(0x3c, 0x06c0);
}

int Audio_InitHardware()
{
    int ec;

    // GPIO Settings
    Codec_SetGpios();

    // Reset Ac97 Link
    ec = Ac97_Reset();
    if (ec != 0) goto Exit;

    // Init audio codec
    ec = Codec_Init();
    if (ec != 0) goto Exit;

Exit:
    return ec;
}

// Close audio
void Audio_Close()
{
}

int PlaySound()
{
    int i;
    int sampleLen = sizeof(c_testSample) / 2; // Each sample used 2 bytes
    unsigned short *pData = (unsigned short *)c_testSample;
    unsigned long sample;

    for (i = 0; i < sampleLen; i++) {
       // TODO: Wait so that the FIFO can be filled with any new data.
       while ((POSR & 0x04) == 0) ;

       // For AC97, left and right channel must be both filled.
       // They are same in the test sample audio.
       // PCDR is a 32 bit FIFO.
       // Bit31-16 for right channel, bit15-0 for left channel;
       sample = pData[i];
       sample |= sample << 16;
       PCDR = sample;
    }
    return 0;
}

int AudioInit(void)
{
    int ec;
    ec = Audio_InitHardware();
    if (ec != 0/*NOERROR*/) {
        printk("Error in initilizing audio hardware.\n");
        return 1;
    }
    Codec_OpenAc97DAC();
    Codec_SetSampleRate(TEST_SAMPLE_RATE, STATE_AUDIOOUT);
//    printk("=====Succeed in initilizing audio hardware. Now playing...=====\n");

    return 0;
}
